/**
 * Copyright 2019 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.fos.bos;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import com.baidubce.auth.DefaultBceCredentials;
import com.baidubce.auth.DefaultBceSessionCredentials;
import com.baidubce.services.bos.BosClient;
import com.baidubce.services.bos.BosClientConfiguration;
import com.baidubce.services.bos.model.BucketSummary;
import com.baidubce.services.bos.model.DeleteMultipleObjectsRequest;
import com.baidubce.services.bos.model.DeleteMultipleObjectsResponse;
import com.baidubce.services.bos.model.Errors;
import com.baidubce.services.bos.model.ListBucketsResponse;
import com.baidubce.services.bos.model.ListObjectsRequest;
import com.baidubce.services.bos.model.ObjectMetadata;
import com.baidubce.services.bos.model.PutObjectRequest;
import com.jianggujin.fos.JBucket;
import com.jianggujin.fos.JClient;
import com.jianggujin.fos.JClientWrapper;
import com.jianggujin.fos.JFOSException;
import com.jianggujin.fos.JListObjectsRequest;
import com.jianggujin.fos.JObject;
import com.jianggujin.fos.JObjectListing;
import com.jianggujin.fos.JObjectMetadata;
import com.jianggujin.fos.JObjectSummary;
import com.jianggujin.fos.bos.JBOSConfiguration.JBOSClientConfiguration;
import com.jianggujin.fos.bos.JBOSConfiguration.JBOSSessionTokenConfiguration;
import com.jianggujin.fos.util.JObjectUtils;

import lombok.Getter;
import lombok.NonNull;

/**
 * 百度云客户端
 * 
 * @author jianggujin
 *
 */
@Getter
public class JBOSClient implements JClient {
    private JBOSConfiguration configuration;
    private JClientWrapper<BosClient> wrapper;

    public JBOSClient(@NonNull JBOSConfiguration configuration) {
        this.configuration = configuration;

        BosClientConfiguration config = new BosClientConfiguration();
        JBOSClientConfiguration clientConfiguration = configuration.getClientConfiguration();
        if (clientConfiguration != null) {
            JObjectUtils.copyProperties(configuration, config, true);
        }
        JBOSSessionTokenConfiguration sessionTokenConfiguration = configuration.getSessionTokenConfiguration();
        if (sessionTokenConfiguration != null) {
            String sessionToken = sessionTokenConfiguration.getSessionToken();
            // 已有sessionToken，直接初始化
            if (sessionToken != null) {
                config.setCredentials(new DefaultBceSessionCredentials(configuration.getAccessKeyId(),
                        configuration.getSecretAccessKey(), sessionToken));
                this.wrapper = new JBOSClientWrapper(config);
                return;
            }
            // 自动获取sessionToken
            this.wrapper = new JBOSSecurityClientWrapper(config, new JBOSSecurityTokenService(configuration));
            return;
        }

        config.setCredentials(
                new DefaultBceCredentials(configuration.getAccessKeyId(), configuration.getSecretAccessKey()));
        this.wrapper = new JBOSClientWrapper(config);
    }

    @Override
    public List<JBucket> listBuckets() throws JFOSException {
        ListBucketsResponse bucketsResponse = this.wrapper.getClient().listBuckets();
        List<BucketSummary> buckets = bucketsResponse.getBuckets();
        List<JBucket> jBuckets = new ArrayList<JBucket>(buckets.size());
        for (BucketSummary bucket : buckets) {
            jBuckets.add(new JBOSBucket(bucket.getName()));
        }
        return jBuckets;
    }

    @Override
    public JBucket createBucket(String bucketName) throws JFOSException {
        return new JBOSBucket(this.wrapper.getClient().createBucket(bucketName).getName());
    }

    @Override
    public void deleteBucket(String bucketName) throws JFOSException {
        this.wrapper.getClient().deleteBucket(bucketName);
    }

    @Override
    public boolean doesBucketExist(String bucketName) throws JFOSException {
        return this.wrapper.getClient().doesBucketExist(bucketName);
    }

    @Override
    public JObjectListing listObjects(String bucketName) throws JFOSException {
        return new JBOSObjectListing(this.wrapper.getClient().listObjects(
                new ListObjectsRequest(bucketName).withMaxKeys(JListObjectsRequest.DEFAULT_RETURNED_KEYS_LIMIT)));
    }

    @Override
    public JObjectListing listObjects(String bucketName, String prefix) throws JFOSException {
        return new JBOSObjectListing(this.wrapper.getClient().listObjects(new ListObjectsRequest(bucketName, prefix)
                .withMaxKeys(JListObjectsRequest.DEFAULT_RETURNED_KEYS_LIMIT)));
    }

    @Override
    public JObjectListing listObjects(JListObjectsRequest request) throws JFOSException {
        return new JBOSObjectListing(this.wrapper.getClient()
                .listObjects(new ListObjectsRequest(request.getBucketName(), request.getPrefix())
                        .withMaxKeys(request.getMaxKeys()).withMarker(request.getMarker())
                        .withDelimiter(request.getDelimiter())));
    }

    @Override
    public JObjectMetadata getObjectMetadata(String bucketName, String key) throws JFOSException {
        return new JBOSObjectMetadata(this.wrapper.getClient().getObjectMetadata(bucketName, key));
    }

    @Override
    public void deleteObject(JObjectSummary summary) throws JFOSException {
        this.wrapper.getClient().deleteObject(summary.getBucketName(), summary.getKey());
    }

    @Override
    public void deleteObject(String bucketName, String key) throws JFOSException {
        this.wrapper.getClient().deleteObject(bucketName, key);
    }

    @Override
    public List<String> deleteObjects(String bucketName, List<String> keys) throws JFOSException {
        DeleteMultipleObjectsRequest request = new DeleteMultipleObjectsRequest();
        request.setBucketName(bucketName);
        request.setObjectKeys(keys);
        DeleteMultipleObjectsResponse deleteObjectsResult = this.wrapper.getClient().deleteMultipleObjects(request);
        List<Errors> errors = deleteObjectsResult.getErrors();
        List<String> result = new ArrayList<String>(errors.size());
        for (Errors error : errors) {
            result.add(error.getKey());
        }
        return result;
    }

    @Override
    public void copyObject(String sourceBucketName, String sourceKey, String destinationBucketName,
            String destinationKey) throws JFOSException {
        this.wrapper.getClient().copyObject(sourceBucketName, sourceKey, destinationBucketName, destinationKey);
    }

    @Override
    public JObject getObject(String bucketName, String key) throws JFOSException {
        return new JBOSObject(this.wrapper.getClient().getObject(bucketName, key));
    }

    @Override
    public JObjectMetadata getObject(String bucketName, String key, File file) throws JFOSException {
        return new JBOSObjectMetadata(this.wrapper.getClient().getObject(bucketName, key, file));
    }

    @Override
    public void putObject(String bucketName, String key, InputStream input) throws JFOSException {
        this.wrapper.getClient().putObject(bucketName, key, input, null);
    }

    @Override
    public void putObject(String bucketName, String key, InputStream input, String contentType) throws JFOSException {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        if (contentType != null) {
            objectMetadata.setContentType(contentType);
        }
        this.wrapper.getClient().putObject(bucketName, key, input, objectMetadata);
    }

    @Override
    public void putObject(String bucketName, String key, File file) throws JFOSException {
        this.wrapper.getClient().putObject(bucketName, key, file);
    }

    @Override
    public void putObject(String bucketName, String key, File file, String contentType) throws JFOSException {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        if (contentType != null) {
            objectMetadata.setContentType(contentType);
        }
        PutObjectRequest request = new PutObjectRequest(bucketName, key, file);
        request.setObjectMetadata(objectMetadata);
        this.wrapper.getClient().putObject(request);
    }

    @Override
    public String generateUrl(String bucketName, String key, int expires) throws JFOSException {
        // 生成以GET方法访问的签名URL，访客可以直接通过浏览器访问相关内容。
        return this.wrapper.getClient().generatePresignedUrl(bucketName, key, expires).toString();
    }

    @Override
    public void destory() {
        if (this.wrapper != null) {
            this.wrapper.destory();
        }
    }
}
